import re
import os
import sys

PATTERN = r"""
public\s+class\s+(?P<class_name>\w+)\s*<:\s*\w+\s*{\s*//\s*Return:\s*(?P<return_type>\w+) # Class name and return type
.*?  # Skip until constructor
public\s+(?P<class_name2>\w+)\(  # Constructor start
(?P<members>.*?) # Parameters
\)\s*\{
"""

class Event:
    def __init__(self, name, return_type, members):
        self.name = name
        self.return_type = return_type
        self.members = members

    @property
    def handler_type(self) -> str:
        return f"{self.name}Handler"

    @property
    def handler_return_type(self) -> str:
        if self.return_type == "Unit":
            return "Unit"
        else:
            return f"EventResponse<{self.return_type}>"

    @property
    def handler_list(self) -> str:
        return f"{self.name[0].lower()}{self.name[1:]}Handlers"


def extract_events(file_path) -> list[Event]:
    """Extract all public struct names from the given file.
    Args:
        file_path (str): Path to the file to parse
    Returns:
        list: List of struct names found
    """
    events = []

    try:
        with open(file_path, 'r') as f:
            content = f.read()

            # Find all public struct declarations using regex
            # Pattern matches: public struct StructName {
            matches = re.finditer(PATTERN, content, re.DOTALL | re.VERBOSE)

            for match in matches:
                struct_name = match.group('class_name').strip()
                return_type = match.group('return_type').strip()
                # print(match.group('members'))
                members = match.group('members').strip()
                events.append(Event(struct_name, return_type, members))

    except FileNotFoundError:
        print(f"Error: File not found - {file_path}")
    except Exception as e:
        print(f"Error reading file: {e}")

    if not events:
        print("No event names found in the input file.")
        sys.exit(1)

    print(f"Found event names: {', '.join([event.name for event in events])}")

    return events

event_handler_manager_code_header = '''
/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2025. All rights reserved.
 */
//-----------------------------------------------
// This file is generated by gen.py
// Never modify it directly
//-----------------------------------------------
package magic.interaction

import magic.core.interaction.*
import magic.core.agent.{Agent, AgentRequest, AgentResponse}
import magic.core.tool.{ToolRequest, ToolResponse}
import magic.core.model.{ChatRequest, ChatResponse}
import magic.utils.randomString

import std.collection.ArrayList
'''

event_handler_manager_header = """
public class EventHandlerManager {
    protected static let CLASS_NAME = "event-handler-manager"

    public EventHandlerManager(
        private let name!: String = randomString(size: 8)
    ) { }

    private static var _global: Option<EventHandlerManager> = None
    public static prop global: EventHandlerManager {
        get() {
            if (let Some(v) <- _global) {
                return v
            } else {
                let v = EventHandlerManager(name: "Global event manager")
                _global = v
                return v
            }
        }
    }

"""

#
# For event handlers not returning values
#
event_handler_template_1 = """
    // Handler for {struct_name} events
    private let {list_field} = ArrayList<{handler_type}>()

    // Add addHandler method
    public func addHandler(handler: {handler_type}): Unit {{
        this.{list_field}.add(handler)
    }}

    // Add handle method
    protected func handle(evt: {struct_name}): {return_type} {{
        for (handler in this.{list_field}) {{
           handler(evt)
       }}
    }}
"""

#
# For event handlers returning values
#
event_handler_template_2 = """
    // Handler for {struct_name} events
    private let {list_field} = ArrayList<{handler_type}>()

    // Add addHandler method
    public func addHandler(handler: {handler_type}): Unit {{
        this.{list_field}.add(handler)
    }}

    // Add handle method
    protected func handle(evt: {struct_name}): {return_type} {{
        for (handler in this.{list_field}) {{
           match (handler(evt)) {{
               case Continue => ()
               case Continue(v) => return Continue(v)
               case Terminate(v) => return Terminate(v)
           }}
       }}
       return Continue
    }}
"""

def generate_event_handler_manager(events: list[Event]):
    """Generate the EventHandlerManager code for the given struct names.
    Args:
        events (list): List of struct names to generate handlers for
    Returns:
        str: The generated code
    """
    code = [event_handler_manager_code_header]

    # Generate type aliases for each event handler first
    for event in events:
        code.append(f"public type {event.handler_type} = ({event.name}) -> {event.handler_return_type}")

    code.append(event_handler_manager_header)

    # Generate private fields and methods for each struct
    for event in events:
        args = {
            "struct_name": event.name,
            "return_type": event.handler_return_type,
            "handler_type": event.handler_type,
            "list_field": event.handler_list
        }
        if event.return_type == "Unit":
            code.append(event_handler_template_1.format(**args))
        else:
            code.append(event_handler_template_2.format(**args))

    # Generate the merge method
    code.append(f"    protected func merge(another: EventHandlerManager): Unit {{")
    for event in events:
        list_field = event.handler_list
        code.append(f"        this.{list_field}.add(all: another.{list_field})")

    code.append("    }")

    code.append("}")

    return '\n'.join(code)

event_stream_visitor_code_header = '''
/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2025. All rights reserved.
 */
//-----------------------------------------------
// This file is generated by gen.py
// Never modify it directly
//-----------------------------------------------
package magic.interaction

import magic.core.interaction.*

'''

def generate_event_stream_visitor(events):
    """Generate the EventHandlerManager code for the given struct names.
    Args:
        events (list): List of struct names to generate handlers for
    Returns:
        str: The generated code
    """
    code = [event_stream_visitor_code_header]

    code.append("public abstract class EventStreamVisitor {")
    code.append("    public EventStreamVisitor(")
    code.append("        private let events: EventStream")
    code.append("    ) { }")

    code.append("")
    code.append("    public func start(): Unit {")
    code.append("        for (event in this.events) {")
    code.append("            this.onEvent(event)")
    code.append("        }")
    code.append("    }")
    code.append("")

    # Generate the merge method
    code.append("    private func onEvent(event: InteractionEvent): Unit {")
    code.append("        match (event.kind) {")
    for event in events:
        event_kind = event.name.replace("Event", "")

        code.append(f"           case EventKind.{event_kind} =>")
        code.append(f"                this.on((event as {event.name}).getOrThrow())")
    code.append("           case EventKind.Sentinel => throw UnsupportedException('Unreachable')")
    code.append("        }")
    code.append("    }")

    # Generate on methods for each event
    for event in events:
        code.append(f"    open public func on(event: {event.name}): Unit {{ }}")
        code.append("")

    code.append("}")

    return '\n'.join(code)

def write_file(output_path, content):
    """Write the generated content to the output file.

    Args:
        output_path (str): Path to the output file
        content (str): Content to write
    """
    try:
        with open(output_path, 'w') as f:
            f.write(content)
        print(f"Successfully generated {output_path}")
    except Exception as e:
        print(f"Error writing to file: {e}")

def main():
    script_dir = os.path.dirname(os.path.abspath(__file__))

    # Input and output file paths
    input_file = os.path.join(script_dir, "./events.cj")

    # Extract struct names
    events = extract_events(input_file)

    # Generate handler code
    code = generate_event_handler_manager(events)
    # Write to output file
    write_file(os.path.join(script_dir, "./event_handler_manager.cj"), code)

    # Generate event visitor code
    code = generate_event_stream_visitor(events)
    # Write to output file
    write_file(os.path.join(script_dir, "./event_stream_visitor.cj"), code)

if __name__ == "__main__":
    main()
